# -*- coding: utf-8 -*-

# @File   : json.py
# @Author : Yuvv
# @Date   : 2018/1/6

import os
import json
from django.utils.translation import gettext as _
from django.utils.encoding import smart_text
from bson.objectid import ObjectId

from apps.storage.models.data import Template, DataMeta
from apps.storage.models.file import ObjectFile
from apps.storage.models.template import TemplateField, TemplateFieldEnum, TemplateJSONEncoder
from apps.storage.models.file import ObjectFileType, ObjectContentType
from apps.storage.utils.serializers.data import (
    DataContent, DataFieldContainer,
    DataFieldTableRow, DataFieldTable
)

from .dummy import *
from .common import *


class FieldSerializer:
    def __init__(self, field_name: str, field: TemplateField, owner_path: str = None):
        if isinstance(field, dict):
            field = TemplateField.from_dict(field)
            field.clean()
        self.field = field
        self.name = field_name
        if self.name and owner_path:
            self.path = owner_path + '.' + self.name
        else:
            self.path = self.name or owner_path

    def get_template(self):
        return {f_type_key: self.field.t.description,
                f_required_key: self.field.r}

    def get_dummy(self, dummy_value=None):
        return f_dummy_fmt % {'name': self.name,
                              'value': dummy_value}

    def serialize(self, field_value) -> dict:
        return field_value

    def deserialize(self, o, required_type=None, **kwargs):
        """
        反序列化
        :param o: 待处理对象（value）
        :param required_type:  期待的待处理对象类型，如果有多个则第一个必须为最佳的，
            o 是 None 时将使用第一个对象初始化
        :param kwargs: `wd`, `owner_id`, `meta_id` 可以设置
        :return: 返回处理后的对象
        """
        if isinstance(o, str) and p_dummy_pattern.match(o):
            raise ValueError('%s field\'s value `%s` is dummy, change or delete it.' % (self.name, str(o)))
        if self.field.r and not o and not isinstance(o, (int, float, bool)):  # 防止 0 和 False
            raise ValueError('%s field required!' % self.name)
        if o is None:  # 非必填项可能有 None 的情况
            # return None
            if self.field.t == TemplateFieldEnum.NUMBER:
                return o
            if required_type:  # 返回一个默认空对象
                return required_type[0]() if isinstance(required_type, (list, tuple)) else required_type()
        if required_type and not isinstance(o, required_type):
            raise TypeError('`%s` require %s, %s given.' % (self.name, required_type, type(o).__name__))
        return o


class StringFieldSerializer(FieldSerializer):
    """字符串"""

    def get_dummy(self, dummy_value=_('Material Genetic Engineering')):
        return super().get_dummy(dummy_value)


class NumberFieldSerializer(FieldSerializer):
    """数值"""

    def get_template(self):
        base = super().get_template()
        if self.field.misc:
            base[f_unit_key] = self.field.misc.get('unit')
        return base

    def get_dummy(self, dummy_value=3.14159):
        return super().get_dummy(dummy_value)

    def deserialize(self, o, required_type=(float, int, str), **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        if isinstance(o, str):
            if p_integer_pattern.match(o):
                o = int(o)
            else:
                o = float(o)
        return o


class RangeFieldSerializer(FieldSerializer):
    """范围"""

    def get_template(self):
        base = super().get_template()
        base[f_range_type_key] = f_range_type_interval if self.field.misc.get('type') else f_range_type_error
        if 'unit' in self.field.misc:
            base[f_unit_key] = self.field.misc.get('unit')
        return base

    def get_dummy(self, dummy_value: (tuple, list) = (0.618, 3.14)):
        if self.field.misc['type']:  # 误差型
            dummy_dict = {'val': f_dummy_fmt % {'name': f_range_val, 'value': dummy_value[0]},
                          'err': f_dummy_fmt % {'name': f_range_err, 'value': dummy_value[1]}}
        else:  # 区间型
            dummy_dict = {'lb': f_dummy_fmt % {'name': f_range_lb, 'value': dummy_value[0]},
                          'ub': f_dummy_fmt % {'name': f_range_ub, 'value': dummy_value[1]}}
        return dummy_dict

    def deserialize(self, o, required_type=dict, **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        val_serializer = NumberFieldSerializer(self.name, TemplateField(TemplateFieldEnum.NUMBER,
                                                                        self.field.r))
        if self.field.misc.get('type'):
            if self.field.r and 'val' not in o and 'err' not in o:
                raise ValueError('%s - %s should have `val` and `err` field.' % (self.name, f_range_type_error))
            return dict(val=val_serializer.deserialize(o.get('val')),
                        err=val_serializer.deserialize(o.get('err')))
        else:
            if self.field.r and 'lb' not in o and 'ub' not in o:
                raise ValueError('%s - %s should have `lb` and `ub` field.' % (self.name, f_range_type_error))
            return dict(lb=val_serializer.deserialize(o.get('lb')),
                        ub=val_serializer.deserialize(o.get('ub')))


class ImageFieldSerializer(FieldSerializer):
    """图片"""

    def get_template(self):
        base = super().get_template()
        base[f_multiple_key] = self.field.misc.get('multi', False)
        return base

    def get_dummy(self, dummy_value='images/a.png'):
        dummy_list = [f_dummy_fmt % {'name': f_image_path, 'value': dummy_value}]
        if self.field.misc.get('multi'):
            dummy_list.append(f_dummy_fmt % {'name': f_image_path, 'value': 'another/' + dummy_value})
        return dummy_list

    def serialize(self, field_value: list):
        if not isinstance(field_value, list):
            raise ValueError('%s\'s value\'s value should be a list, %s given' % (self.path, type(field_value)))
        if self.field.misc.get('multi'):
            return super().serialize(field_value)
        return super().serialize(field_value[0] if field_value else None)

    def deserialize(self, o, required_type=(list, str), **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        if isinstance(o, (str, ObjectId)):
            o = [o]

        wd = kwargs.get('wd')
        owner_id = kwargs.get('owner_id')
        meta_id = kwargs.get('meta_id')
        image_urls = list()

        for img_uri in o:
            m = p_of_url_pattern.match(img_uri)
            if m:
                img = ObjectFile.objects.filter(file=m.group('name')).first()
                if img:
                    img.misc['meta_id'] = meta_id
                    img.misc['owner_id'] = str(owner_id)
                    img.misc['path'] = self.path
                    img.save(update_fields=('misc',))
                    image_urls.append(img.get_file_url())
                continue
            img_path = os.path.join(wd, img_uri)
            if not os.path.exists(img_path):
                raise ValueError('%s: image `%s` not found' % (self.name, img_uri))
            with open(img_path, 'rb') as img_file:
                img_f = ObjectFile.add(ObjectFileType.DATA_CONTENT, ObjectContentType.DATA_IMAGE, img_file,
                                       name=img_file.name, author=kwargs.get('author', '_system'),
                                       meta_id=meta_id, owner_id=str(owner_id), path=self.path)
                image_urls.append(img_f.get_file_url())

        return image_urls


class FileFieldSerializer(FieldSerializer):
    """文件"""

    def get_template(self):
        base = super().get_template()
        base[f_multiple_key] = self.field.misc.get('multi', False)
        return base

    def get_dummy(self, dummy_value='file/a.txt'):
        dummy_list = [f_dummy_fmt % {'name': f_file_path, 'value': dummy_value}]
        if self.field.misc.get('multi'):
            dummy_list.append(f_dummy_fmt % {'name': f_file_path, 'value': 'another/' + dummy_value})
        return dummy_list

    def serialize(self, field_value: list):
        if not isinstance(field_value, list):
            raise ValueError('%s\'s value\'s value should be a list, %s given' % (self.name,
                                                                                  type(field_value).__name__))
        if self.field.misc.get('multi'):
            return super().serialize(field_value)
        return super().serialize(field_value[0] if field_value else None)

    def deserialize(self, o, required_type=(list, str), **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        if isinstance(o, (str, ObjectId)):
            o = [o]

        wd = kwargs.get('wd')
        owner_id = kwargs.get('owner_id')
        meta_id = kwargs.get('meta_id')
        file_urls = list()

        for f_uri in o:
            m = p_of_url_pattern.match(f_uri)
            if m:
                f_f = ObjectFile.objects.filter(file=m.group('name')).first()
                if f_f:
                    f_f.misc['meta_id'] = meta_id
                    f_f.misc['owner_id'] = str(owner_id)
                    f_f.misc['path'] = self.path
                    f_f.save(update_fields=('misc',))
                    file_urls.append(f_f.get_file_url())
                continue
            f_path = os.path.join(wd, f_uri)
            if not os.path.exists(f_path):
                raise ValueError('%s: file `%s` not found' % (self.name, f_uri))
            with open(f_path, 'rb') as f_file:
                f_f = ObjectFile.add(ObjectFileType.DATA_CONTENT, ObjectContentType.DATA_FILE, f_file,
                                     name=f_file.name, author=kwargs.get('author', '_system'),
                                     meta_id=meta_id, owner_id=str(owner_id), path=self.path)
                file_urls.append(f_f.get_file_url())

        return file_urls


class ChoiceFieldSerializer(FieldSerializer):
    """候选"""

    def get_template(self):
        base = super().get_template()
        base[f_options_key] = self.field.misc.get('opt')
        grps = self.field.misc.get('grp')
        base[f_groups_key] = {}
        for grp in grps:
            base[f_groups_key][grp['name']] = grp['items']
        return base

    def get_dummy(self, dummy_value=None):
        opt = self.field.misc.get('opt')
        grp = self.field.misc.get('grp')
        if opt:
            sel = opt[0]
        else:
            sel = grp[0]['items'][0]
        dummy_value = dummy_value or sel
        return super().get_dummy(dummy_value)

    def deserialize(self, o, required_type=str, **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        if not o:
            return o
        if o in self.field.misc.get('opt'):
            return o
        for grp in self.field.misc.get('grp'):
            if o in grp['items']:
                return o
        raise ValueError('%s\'s value `%s` not in options.' % (self.name, o))


class ArrayFieldSerializer(FieldSerializer):
    """数组"""

    def __init__(self, field_name: str, field: TemplateField, owner_path: str = None):
        super().__init__(field_name, field, owner_path)
        self._field_serializer = JSONSerializer.get_field_serializer('', self.field.misc, self.path)

    def get_template(self):
        base = super().get_template()
        base[f_array_type_key] = self._field_serializer.get_template()
        return base

    def get_dummy(self, dummy_value=None):
        dummy_list = list()
        if dummy_value is None:
            dummy_list.append(self._field_serializer.get_dummy())
        else:
            dummy_list.append(self._field_serializer.get_dummy(dummy_value))
        return dummy_list

    def serialize(self, field_value):
        if not isinstance(field_value, list):
            raise ValueError('%s\'s value should be a list, %s given' % (self.name, type(field_value)))
        arr = list()
        for item in field_value:
            arr.append(self._field_serializer.serialize(item))
        return super().serialize(arr)

    def deserialize(self, o, required_type=(list, tuple), **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        arr = list()
        for item in o:
            arr.append(self._field_serializer.deserialize(item, **kwargs))
        return arr


class TableFieldSerializer(FieldSerializer):
    """表格"""

    def __init__(self, field_name: str, field: TemplateField, owner_path: str = None):
        super().__init__(field_name, field, owner_path)
        self._head_serializers = dict()
        for h_name in self.field.misc.get('_head'):
            self._head_serializers[h_name] = JSONSerializer.get_field_serializer(h_name,
                                                                                 self.field.misc.get(h_name),
                                                                                 self.path)

    def get_template(self):
        base = super().get_template()
        d = dict()
        for h_name in self.field.misc.get('_head'):
            d[h_name] = self._head_serializers[h_name].get_template()
        base[f_heads_key] = d
        return base

    def get_dummy(self, dummy_value=None):
        row = dict()
        for h_name in self.field.misc.get('_head'):
            row[h_name] = self._head_serializers[h_name].get_dummy()
        rows = list()
        rows.append(row)
        return rows

    def serialize(self, field_value):
        if not isinstance(field_value, ObjectId):
            raise ValueError('%s\'s value must be an ObjectId or dict, not %s' % (self.name,
                                                                                  type(field_value).__name__))

        rows = list()
        for item in DataFieldTableRow.objects(_owner_id=field_value):
            row = dict()
            for h_name in self.field.misc.get('_head'):
                row[h_name] = self._head_serializers[h_name].serialize(getattr(item, h_name, None))
            rows.append(row)
        return rows

    def deserialize(self, o, required_type=(list, tuple), **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        owner_id = kwargs.get('owner_id')
        tid = kwargs.get('tid')
        meta_id = kwargs.get('meta_id')
        table_id = ObjectId()
        kwargs['owner_id'] = table_id
        try:
            table = DataFieldTable(id=table_id, head=self.field.misc.get('_head'),
                                   _meta_id=meta_id, _tid=tid, _owner_id=owner_id, _path=self.path)
            for row in o:
                true_row = dict()
                for h_name in self.field.misc.get('_head'):
                    true_row[h_name] = self._head_serializers[h_name].deserialize(row[h_name], **kwargs)
                DataFieldTableRow.add(meta_id, tid, table_id, self.path, true_row)
            table.save()
        except Exception as ex:
            for row in DataFieldTableRow.objects(_owner_id=table_id):
                row.delete()
            raise ex
        else:
            return table.id


class ContainerFieldSerializer(FieldSerializer):
    """容器"""

    def __init__(self, field_name: str, field: TemplateField, owner_path: str = None):
        super().__init__(field_name, field, owner_path)
        self._field_serializers = dict()
        for f_name in self.field.misc.get('_ord'):
            self._field_serializers[f_name] = JSONSerializer.get_field_serializer(f_name,
                                                                                  self.field.misc.get(f_name),
                                                                                  self.path)

    def get_template(self):
        base = super().get_template()
        d = dict()
        for f_name in self.field.misc.get('_ord'):
            d[f_name] = self._field_serializers[f_name].get_template()
        base[f_fields_key] = d
        return base

    def get_dummy(self, dummy_value=None):
        ctn = dict()
        for f_name in self.field.misc.get('_ord'):
            ctn[f_name] = self._field_serializers[f_name].get_dummy()
        return ctn

    def serialize(self, field_value):
        if not isinstance(field_value, ObjectId):
            raise ValueError('%s\'s value must be an ObjectId or dict, not %s' % (self.name,
                                                                                  type(field_value).__name__))

        container = DataFieldContainer.objects.get(pk=field_value)
        ctn = dict()
        for f_name in self.field.misc.get('_ord'):
            ctn[f_name] = self._field_serializers[f_name].serialize(getattr(container, f_name, None))
        return ctn

    def deserialize(self, o, required_type=dict, **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        owner_id = kwargs.get('owner_id')
        tid = kwargs.get('tid')
        meta_id = kwargs.get('meta_id')
        ctn_id = ObjectId()
        kwargs['owner_id'] = ctn_id
        try:
            true_ctn = dict()
            for f_name in self.field.misc.get('_ord'):
                true_ctn[f_name] = self._field_serializers[f_name].deserialize(o.get(f_name), **kwargs)
            ctn = DataFieldContainer(id=ctn_id, _meta_id=meta_id, _tid=tid, _owner_id=owner_id, _path=self.path,
                                     **true_ctn)
            ctn.save()
        except Exception as ex:
            for field_cls in (DataFieldTable, DataFieldContainer):
                for field in field_cls.objects(_meta_id=meta_id):
                    field.delete()
            raise ex
        else:
            return ctn.id


class GeneratorFieldSerializer(FieldSerializer):
    """生成器"""

    def __init__(self, field_name: str, field: TemplateField, owner_path: str = None):
        super().__init__(field_name, field, owner_path)
        self._opt_serializers = dict()
        for opt_name in self.field.misc.get('_opt'):
            self._opt_serializers[opt_name] = JSONSerializer.get_field_serializer(opt_name,
                                                                                  self.field.misc.get(opt_name),
                                                                                  self.path)

    def get_template(self):
        base = super().get_template()
        d = dict()
        for opt_name in self.field.misc.get('_opt'):
            d[opt_name] = self._opt_serializers[opt_name].get_template()
        base[f_options_key] = d
        return base

    def get_dummy(self, dummy_value=None):
        opts = dict()
        for opt_name in self.field.misc.get('_opt'):
            opts[opt_name] = self._opt_serializers[opt_name].get_dummy()
        sel_opt = self.field.misc.get('_opt')[0]
        selected = f_dummy_fmt % {'name': f_selected, 'value': sel_opt}
        value = self._opt_serializers[sel_opt].get_dummy()
        return dict(_options=opts, sel=selected, val=value)

    def serialize(self, field_value):
        sel_name = field_value.get('sel')
        if sel_name not in self.field.misc.get('_opt'):
            raise ValueError('%s\'s selected name illegal, %s given but not in options' % (self.path, sel_name))

        sel_val = field_value.get('val')
        return dict(sel=sel_name,
                    val=self._opt_serializers[sel_name].serialize(sel_val))

    def deserialize(self, o, required_type=dict, **kwargs):
        o = super().deserialize(o, required_type, **kwargs)
        gen_sel = o.get('sel')
        if gen_sel not in self.field.misc.get('_opt'):
            raise ValueError('%s\'s value `%s` not in options.' % (self.path, gen_sel))
        gen_val = self._opt_serializers[gen_sel].deserialize(o.get('val'), **kwargs)
        return dict(sel=gen_sel, val=gen_val)


_serializer_map = {
    TemplateFieldEnum.STRING: StringFieldSerializer,
    TemplateFieldEnum.NUMBER: NumberFieldSerializer,
    TemplateFieldEnum.RANGE: RangeFieldSerializer,
    TemplateFieldEnum.IMAGE: ImageFieldSerializer,
    TemplateFieldEnum.FILE: FileFieldSerializer,
    TemplateFieldEnum.CHOICE: ChoiceFieldSerializer,
    TemplateFieldEnum.ARRAY: ArrayFieldSerializer,
    TemplateFieldEnum.TABLE: TableFieldSerializer,
    TemplateFieldEnum.CONTAINER: ContainerFieldSerializer,
    TemplateFieldEnum.GENERATOR: GeneratorFieldSerializer,
}


class JSONSerializer:
    @staticmethod
    def serialize(template: Template, queryset=None, path_or_fp=None,
                  ensure_ascii=False, indent=2):
        serializer = JSONSerializer(template, queryset)
        t = template.to_dict(True)
        t['content'] = serializer.get_template()
        ds = list()
        if queryset is None:
            ds.append(serializer.get_dummy_obj())
        else:
            for obj in queryset:
                ds.append(serializer.get_serialized_obj(obj))
        rs = dict(template=t, dataset=ds)
        if path_or_fp:
            return serializer._dump(rs, path_or_fp, ensure_ascii=ensure_ascii, cls=TemplateJSONEncoder, indent=indent)
        return serializer._dumps(rs, ensure_ascii=ensure_ascii, cls=TemplateJSONEncoder, indent=indent)

    @staticmethod
    def deserialize(path_or_fp):
        if hasattr(path_or_fp, 'read'):
            obj = JSONSerializer._loads(path_or_fp.read())
            wd = os.path.dirname(os.path.abspath(path_or_fp.name))
        else:
            obj = JSONSerializer._load(path_or_fp)
            wd = os.path.dirname(path_or_fp)
        template = obj.get('template')
        t = Template.objects.get(pk=template.get('id'))
        serializer = JSONSerializer(t)
        dataset = obj.get('dataset')
        if not isinstance(dataset, list):
            raise ValueError('dataset should be a list, `%s` given' % type(dataset))

        rs = list()
        try:
            for d in dataset:
                rs.append(serializer.get_deserialized_obj(d, wd=wd))
        except Exception as ex:
            for dm in rs:
                dm.delete()  # fixme: 效率问题？？？
            raise ex
        else:
            return rs

    @staticmethod
    def _dump(obj, path_or_fp, **kwargs):
        if hasattr(path_or_fp, 'write'):
            json.dump(obj, path_or_fp, **kwargs)
        else:
            with open(path_or_fp, 'w', encoding='utf-8') as f:
                json.dump(obj, f, **kwargs)

    @staticmethod
    def _dumps(obj, **kwargs) -> str:
        return json.dumps(obj, **kwargs)

    @staticmethod
    def _load(path, **kwargs):
        with open(path, encoding='utf-8') as fp:
            return json.load(fp, **kwargs)

    @staticmethod
    def _loads(s, **kwargs):
        return json.loads(s, encoding='utf-8', **kwargs)

    @staticmethod
    def _fill_data_meta(obj: DataMeta, exclude=(), only=None) -> dict:
        dm = obj.to_dict(True)
        if only:
            new_dm = dict()
            for name in only:
                new_dm[name] = dm.get(name)
            return new_dm
        else:
            for f_name in exclude:
                if f_name in dm:
                    dm.pop(f_name)
            return dm

    @staticmethod
    def get_field_serializer(field_name: str, field: TemplateField, owner_path: str = None) -> FieldSerializer:
        if isinstance(field, dict):
            field = TemplateField.from_dict(field)
        return _serializer_map[field.t](field_name, field, owner_path)

    def __init__(self, template: Template, queryset=None):
        if not isinstance(template, Template):
            raise TypeError('%s object is not instance of Template.' % type(template))
        self._t = template
        self._qs = queryset
        self._get_serializers()

    def _get_serializers(self):
        self.serializers = dict()
        t_content = self._t.content
        for field_name in t_content['_ord']:
            field = TemplateField.from_dict(t_content[field_name])
            self.serializers[field_name] = self.get_field_serializer(field_name, field)

    def get_template(self) -> dict:
        d = dict()
        for name, serializer in self.serializers.items():
            d[name] = serializer.get_template()
        return d

    def get_dummy_obj(self):
        data_meta = {
            'title': f_dummy_fmt % {'name': d_title, 'value': d_title_eg},
            'keywords': [f_dummy_fmt % {'name': d_keywords, 'value': d_keywords_eg}],
            'source': {
                'source': f_dummy_fmt % {'name': d_src_s, 'value': d_src_s_eg},
                'reference': f_dummy_fmt % {'name': d_src_r, 'value': d_src_r_eg},
                'project': f_dummy_fmt % {'name': d_src_p, 'value': d_src_p_eg},
                'others': f_dummy_fmt % {'name': d_src_o, 'value': d_src_o_eg}
            },
            'abstract': f_dummy_fmt % {'name': d_abstract, 'value': d_abstract_eg},
            'doi': f_dummy_fmt % {'name': d_doi, 'value': d_doi_eg},
            'purpose': f_dummy_fmt % {'name': d_purpose, 'value': d_purpose_eg}
        }
        data_content = dict()
        for name, serializer in self.serializers.items():
            data_content[name] = serializer.get_dummy()
        return dict(meta=data_meta, content=data_content)

    def get_serialized_obj(self, obj: DataMeta):
        data_content = dict()
        dc = DataContent.objects.get(pk=obj.dc_id)
        for name, serializer in self.serializers.items():
            data_content[name] = serializer.serialize(getattr(dc, name, None))
        data_meta = self._fill_data_meta(obj, ('id', 'category', 'tid', 'dc_id'))
        return dict(id=obj.id, meta=data_meta, content=data_content)

    def get_deserialized_obj(self, obj: dict, **kwargs) -> DataMeta:
        dm = None
        try:
            dc_id = ObjectId()
            meta = self._get_deserialized_meta(obj.get('meta'))
            if not isinstance(meta, dict):
                raise ValueError('Data meta must be a dict, %s given' % type(meta))
            dm = DataMeta.add(meta.get('title'), self._t.category.id, self._t.id,
                              source=meta.get('source'), keywords=meta.get('keywords'), content_id=dc_id,
                              doi=meta.get('doi'), abstract=meta.get('abstract'), purpose=meta.get('purpose'), author=self._t.author)

            dc = obj.get('content')
            content = dict(_id=dc_id)
            for name, serializer in self.serializers.items():
                content[name] = serializer.deserialize(dc.get(name),
                                                       owner_id=dc_id, meta_id=dm.id, tid=self._t.id, **kwargs)
            DataContent.add(dm.id, self._t.id, content)
        except Exception as ex:
            if dm and dm.pk:
                dm.delete()
            raise ex
        else:
            return dm

    def _get_deserialized_meta(self, meta: dict) -> dict:
        def get_true_value(k, v):
            if isinstance(v, str) and p_dummy_pattern.match(v):
                raise ValueError('%s\'s value `%s` is dummy, change or delete it.' % (k, v))
            if v:
                return smart_text(v)
            return v

        meta['title'] = get_true_value('title', meta.get('title'))
        meta['doi'] = get_true_value('doi', meta.get('doi'))
        meta['abstract'] = get_true_value('abstract', meta.get('abstract'))
        meta['purpose'] = get_true_value('purpose', meta.get('purpose'))
        if isinstance(meta.get('keywords'), (list, tuple)):
            meta['keywords'] = [get_true_value('keywords', word) for word in meta.get('keywords')]
        else:
            meta['keywords'] = get_true_value('keywords', meta.get('keywords'))
        if meta.get('source'):
            source = meta.get('source')
            if not isinstance(source, dict):
                raise ValueError('meta must be a dict, `%s` given' % type(source))
            meta['source'] = dict(source=get_true_value('source', source.get('source')),
                                  reference=get_true_value('reference', source.get('reference')),
                                  project=get_true_value('project', source.get('project')),
                                  others=get_true_value('others', source.get('others')))

        return meta


# lyp新添加的
